/* Arduino Sd2Card Library
* Copyright (C) 2009 by William Greiman
*
* This file is part of the Arduino Sd2Card Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino Sd2Card Library.  If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef __SD2CARD__
#define __SD2CARD__

/**
* \file
* Sd2Card class
*/
#include "SdInfo.h"
#include "HardwareSPI.h"

/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */
uint8 const SPI_FULL_SPEED = 0;
/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */
uint8 const SPI_HALF_SPEED = 1;
/** Set SCK rate to F_CPU/8. Sd2Card::setSckRate(). */
uint8 const SPI_QUARTER_SPEED = 2;

//------------------------------------------------------------------------------
/** Protect block zero from write if nonzero */
#define SD_PROTECT_BLOCK_ZERO 1
/** init timeout ms */
uint16 const SD_INIT_TIMEOUT = 2000;
/** erase timeout ms */
uint16 const SD_ERASE_TIMEOUT = 10000;
/** read timeout ms */
uint16 const SD_READ_TIMEOUT = 300;
/** write time out ms */
uint16 const SD_WRITE_TIMEOUT = 600;

//------------------------------------------------------------------------------
// SD card errors
/** timeout error for command CMD0 */
uint8 const SD_CARD_ERROR_CMD0 = 0X1;
/** CMD8 was not accepted - not a valid SD card*/
uint8 const SD_CARD_ERROR_CMD8 = 0X2;
/** card returned an error response for CMD17 (read block) */
uint8 const SD_CARD_ERROR_CMD17 = 0X3;
/** card returned an error response for CMD24 (write block) */
uint8 const SD_CARD_ERROR_CMD24 = 0X4;
/**  WRITE_MULTIPLE_BLOCKS command failed */
uint8 const SD_CARD_ERROR_CMD25 = 0X05;
/** card returned an error response for CMD58 (read OCR) */
uint8 const SD_CARD_ERROR_CMD58 = 0X06;
/** SET_WR_BLK_ERASE_COUNT failed */
uint8 const SD_CARD_ERROR_ACMD23 = 0X07;
/** card's ACMD41 initialization process timeout */
uint8 const SD_CARD_ERROR_ACMD41 = 0X08;
/** card returned a bad CSR version field */
uint8 const SD_CARD_ERROR_BAD_CSD = 0X09;
/** erase block group command failed */
uint8 const SD_CARD_ERROR_ERASE = 0X0A;
/** card not capable of single block erase */
uint8 const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0X0B;
/** Erase sequence timed out */
uint8 const SD_CARD_ERROR_ERASE_TIMEOUT = 0X0C;
/** card returned an error token instead of read data */
uint8 const SD_CARD_ERROR_READ = 0X0D;
/** read CID or CSD failed */
uint8 const SD_CARD_ERROR_READ_REG = 0X0E;
/** timeout while waiting for start of read data */
uint8 const SD_CARD_ERROR_READ_TIMEOUT = 0X0F;
/** card did not accept STOP_TRAN_TOKEN */
uint8 const SD_CARD_ERROR_STOP_TRAN = 0X10;
/** card returned an error token as a response to a write operation */
uint8 const SD_CARD_ERROR_WRITE = 0X11;
/** attempt to write protected block zero */
uint8 const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X12;
/** card did not go ready for a multiple block write */
uint8 const SD_CARD_ERROR_WRITE_MULTIPLE = 0X13;
/** card returned an error to a CMD13 status check after a write */
uint8 const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X14;
/** timeout occurred during write programming */
uint8 const SD_CARD_ERROR_WRITE_TIMEOUT = 0X15;
/** incorrect rate selected */
uint8 const SD_CARD_ERROR_SCK_RATE = 0X16;

//------------------------------------------------------------------------------
// card types
/** Standard capacity V1 SD card */
uint8 const SD_CARD_TYPE_SD1 = 1;
/** Standard capacity V2 SD card */
uint8 const SD_CARD_TYPE_SD2 = 2;
/** High Capacity SD card */
uint8 const SD_CARD_TYPE_SDHC = 3;

//------------------------------------------------------------------------------
/**
* \class Sd2Card
* \brief Raw access to SD and SDHC flash memory cards.
*/
class Sd2Card {
public:
	/** Construct an instance of Sd2Card. */
	Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {}
	uint32 cardSize(void);
	uint8 erase(uint32 firstBlock, uint32 lastBlock);
	uint8 eraseSingleBlockEnable(void);
	/**
	* \return error code for last error. See Sd2Card.h for a list of error codes.
	*/
	uint8 errorCode(void) const {return errorCode_;}
	/** \return error data for last error. */
	uint8 errorData(void) const {return status_;}
	/**
	* Initialize an SD flash memory card with default clock rate and chip
	* select pin.  See sd2Card::init(uint8 sckRateID, uint8 chipSelectPin).
	*/
	uint8 init(void) {
		//    return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN);
		return false;
	}

	/**
	* Initialize an SD flash memory card with the selected SPI clock rate
	* and the default SD chip select pin.
	* See sd2Card::init(uint8 sckRateID, uint8 chipSelectPin).
	*/
	uint8 init(uint8 sckRateID) {
		//    return init(sckRateID, SD_CHIP_SELECT_PIN);
		return false;
	}

	uint8 init(HardwareSPI *);
	void partialBlockRead(uint8 value);
	/** Returns the current value, true or false, for partial block read. */
	uint8 partialBlockRead(void) const {return partialBlockRead_;}
	uint8 readBlock(uint32 block, uint8* dst);
	uint8 readData(uint32 block,
		uint16 offset, uint16 count, uint8* dst);
	/**
	* Read a cards CID register. The CID contains card identification
	* information such as Manufacturer ID, Product name, Product serial
	* number and Manufacturing date. */
	uint8 readCID(cid_t* cid) {
		return readRegister(CMD10, cid);
	}
	/**
	* Read a cards CSD register. The CSD contains Card-Specific Data that
	* provides information regarding access to the card's contents. */
	uint8 readCSD(csd_t* csd) {
		return readRegister(CMD9, csd);
	}
	void readEnd(void);
	uint8 setSckRate(uint8 sckRateID);

	/** Return the card type: SD V1, SD V2 or SDHC */
	uint8 type(void) const {return type_;}
	uint8 writeBlock(uint32 blockNumber, const uint8* src);
	uint8 writeData(const uint8* src);
	uint8 writeStart(uint32 blockNumber, uint32 eraseCount);
	uint8 writeStop(void);

private:
	uint32 block_;
	uint8 chipSelectPin_;
	uint8 errorCode_;
	uint8 inBlock_;
	uint16 offset_;
	uint8 partialBlockRead_;
	uint8 status_;
	uint8 type_;

	// private functions
	uint8 cardAcmd(uint8 cmd, uint32 arg) {
		cardCommand(CMD55, 0);
		return cardCommand(cmd, arg);
	}
	uint8 cardCommand(uint8 cmd, uint32 arg);
	void error(uint8 code) {errorCode_ = code;}
	uint8 readRegister(uint8 cmd, void* buf);
	uint8 sendWriteCommand(uint32 blockNumber, uint32 eraseCount);
	void chipSelectHigh(void);
	void chipSelectLow(void);
	void type(uint8 value) {type_ = value;}

	uint8 waitNotBusy(uint32 timeoutMillis);
	uint8 writeData(uint8 token, const uint8* src);
	uint8 waitStartBlock(void);
};
#endif  // Sd2Card_h
